package com.aididi.concurrent;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Delayed;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Auther: zx.xu
 * @Description:
 */
public class ThreadPoolExecuteDemo {
    //corePoolSize（必需）：核心池的大小，这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后，默认情况下，线程池中并没有任何线程，而是等待有任务到来才创建线程去执行任务，除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法，从这2个方法的名字就可以看出，是预创建线程的意思，即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下，在创建了线程池后，线程池中的线程数为0，当有任务来之后，就会创建一个线程去执行任务，当线程池中的线程数目达到corePoolSize后，就会把到达的任务放到缓存队列当中；
    //maximumPoolSize（必需）：线程池最大线程数，这个参数也是一个非常重要的参数，它表示在线程池中最多能创建多少个线程；
    //keepAliveTime（必需）：线程最大空闲时间,表示线程没有任务执行时最多保持多久时间会终止。默认情况下，只有当线程池中的线程数大于corePoolSize时，keepAliveTime才会起作用，直到线程池中的线程数不大于corePoolSize，即当线程池中的线程数大于corePoolSize时，如果一个线程空闲的时间达到keepAliveTime，则会终止，直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(true)方法，在线程池中的线程数不大于corePoolSize时，keepAliveTime参数也会起作用，直到线程池中的线程数为0；
    //unit（必需）：指定keepAliveTime参数的时间单位。常用的有：TimeUnit.MILLISECONDS（毫秒）、TimeUnit.SECONDS（秒）、TimeUnit.MINUTES（分）。
    //workQueue（必需）：任务队列。通过线程池的execute()方法提交的Runnable对象将存储在该参数中。其采用阻塞队列实现
    //threadFactory（可选）：线程工厂。用于指定为线程池创建新线程的方式。
    //handler（可选）：拒绝策略。当达到最大线程数时需要执行的饱和策略。

    //四种拒绝策略
   // AbortPolicy（默认）：丢弃任务并抛出RejectedExecutionException异常。
   // CallerRunsPolicy：由调用线程处理该任务。
   // DiscardPolicy：丢弃任务，但是不抛出异常。可以配合这种模式进行自定义的处理方式。
   // DiscardOldestPolicy：丢弃队列最早的未处理任务，然后重新尝试执行任务。

    /**
     * 核心线程数配置:
     *  IO密集型（某大厂实践经验） :核心线程数 = CPU核数 / （1-阻塞系数）
     *  或者
     *  CPU密集型：核心线程数 = CPU核数 + 1
     *  IO密集型：核心线程数 = CPU核数 * 2
     *
     * 最大线程数配置：
     * IO密集型经验应用，最大线程设置为 2N+1 (N为CPU数量，下同)
     * CPU密集型应用，最大线程设置为 N+1
     *
     * cpu密集型：
     * CPU密集的意思是该任务需要大量的运算，而没有阻塞，CPU一直全速运行。CPU密集任务只有在真正的多核CPU上才可能得到加速（通过多线程），而在单核CPU上，无论你开几个模拟的多线程该任务都不可能得到加速，因为CPU总的运算能力就那些。
     * CPU 使用率较高（例如:计算一些复杂的运算，逻辑处理等情况）非常多的情况下，线程数一般只需要设置为CPU核心数的线程个数就可以了。 这一类型多出现在开发中的一些业务复杂计算和逻辑处理过程中。
     * 总结：CPU密集型 可以理解为 就是处理繁杂算法的操作，对硬盘等操作不是很频繁，比如一个算法非常之复杂，可能要处理半天，而最终插入到数据库的时间很快。
     *
     * IO密集型：
     * CPU 使用率较低，程序中会存在大量的 I/O 操作占用时间，导致线程空余时间很多，所以通常就需要开CPU核心数两倍的线程。当线程进行 I/O 操作 CPU 空闲时，启用其他线程继续使用 CPU，以提高 CPU 的使用率。例如：数据库交互，文件上传下载，网络传输等。
     * IO密集型可以理解为简单的业务逻辑处理，比如计算1+1=2，但是要处理的数据很多，每一条都要去插入数据库，对数据库频繁操作。
     *
     *
     * 8020原则：
     * 需要根据几个值来决定：
     * tasks ：每秒的任务数，假设为500~1000
     * taskcost：每个任务花费时间，假设为0.1s
     * responsetime：系统允许容忍的最大响应时间，假设为1s
     * corePoolSize = 每秒需要多少个线程处理？
     *  threadcount = tasks/(1/taskcost) =tasks*taskcout =  (500~1000)*0.1 = 50~100 个线程。corePoolSize设置应该大于50
     *   根据8020原则，如果80%的每秒任务数小于800，那么corePoolSize设置为80即可
     * queueCapacity = (coreSizePool/taskcost)*responsetime， 计算可得 queueCapacity = 80/0.1*1 = 80
     * maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)，  计算可得 maxPoolSize = (1000-80)/10 = 92 ，（最大任务数-队列容量）/每个线程每秒处理能力 = 最大线程数
     *
     */
    public static final int CPU_NUM = Runtime.getRuntime().availableProcessors();
    public static void main(String[] args) {

        ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
        //获取当前机器的核数
        ThreadPoolExecutor executor = new ThreadPoolExecutor(CPU_NUM,CPU_NUM*2,5,
                TimeUnit.SECONDS,new LinkedBlockingDeque<>(5),namedThreadFactory,
                new CustomRejectHandler());
        for(int i=0 ;i< 5; i++){
            int finalI = i;
            executor.execute(() -> System.out.println(Thread.currentThread().getName()+"----"+ finalI+","+executor.getQueue().size()));
        }

        executor.shutdown();

    }
 }
